using System;

namespace MFWebSockets.Core.Utilities.Collections
{
    public sealed class BitArray : System.Collections.ICollection, System.Collections.IEnumerable, ICloneable
    {
         const int _ShrinkThreshold = 0x100;//256
        [NonSerialized]
         object _syncRoot = new object();
         int _version;
         const int BitsPerByte = 8;//0x08
         const int BitsPerInt32 = 0x20;//32
         const int BytesPerInt32 = 4;//0x04
         int[] m_array;
         int m_length;
 
        /// <summary>
        /// Prevents the CLR from allowing a creation of this object without parameters
        /// </summary>
        BitArray()
        {
        }

        /// <summary>
        /// Initializes a new instance of the BitArray class that contains bit values copied from the specified BitArray.
        /// </summary>
        /// <param name="bits"></param>
        public BitArray(BitArray bits)
        {
            if (bits == null)
            {
                throw new ArgumentNullException("bits");
            }
            int arrayLength = GetArrayLength(bits.m_length, 0x20);
            this.m_array = new int[arrayLength];
            this.m_length = bits.m_length;
            Array.Copy(bits.m_array, this.m_array, arrayLength);
            this._version = bits._version;
        }

        /// <summary>
        /// Initializes a new instance of the BitArray class that contains bit values copied from the specified array of Booleans.
        /// </summary>
        /// <param name="values"></param>
        public BitArray(bool[] values)
        {
            if (values == null)
            {
                throw new ArgumentNullException("values");
            }
            int valuesLength = values.Length;
            this.m_array = new int[GetArrayLength(valuesLength, 0x20)];
            this.m_length = valuesLength;
            for (int i = 0; i < valuesLength; ++i)
            {
                if (values[i])
                {
                    this.m_array[i / 0x20] |= ((int)1) << (i % 0x20);
                }
            }
            this._version = 0;
        }

        /// <summary>
        /// Initializes a new instance of the BitArray class that contains bit values copied from the specified array of bytes.
        /// </summary>
        /// <param name="bytes"></param>
        public BitArray(byte[] bytes)
        {
            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }
            else if (bytes.Length > 0xfffffff)
            {
                throw new ArgumentException("Argument_ArrayTooLarge", "bytes");
            }
            int bytesLength = bytes.Length;
            this.m_array = new int[GetArrayLength(bytesLength, 4)];
            this.m_length = bytesLength * 8;
            int index = 0;
            int num2 = 0;
            while ((bytes.Length - num2) >= 4)
            {
                this.m_array[index++] = (((bytes[num2] & 0xff) | ((bytes[num2 + 1] & 0xff) << 8)) | ((bytes[num2 + 2] & 0xff) << 0x10)) | ((bytes[num2 + 3] & 0xff) << 0x18);
                num2 += 4;
            }
            switch ((bytes.Length - num2))
            {
                case 1:
                    goto Label_010D;

                case 2:
                    break;

                case 3:
                    this.m_array[index] = (bytes[num2 + 2] & 0xff) << 0x10;
                    break;

                default:
                    goto Label_012E;
            }
            this.m_array[index] |= (bytes[num2 + 1] & 0xff) << 8;
        Label_010D:
            this.m_array[index] |= bytes[num2] & 0xff;
        Label_012E:
            this._version = 0;
        }

        /// <summary>
        /// Initializes a new instance of the BitArray class that contains bit values copied from the specified array of 32-bit integers.
        /// </summary>
        /// <param name="values"></param>
        public BitArray(int[] values)
        {
            if (values == null)
            {
                throw new ArgumentNullException("values");
            }
            else if (values.Length > 0x3ffffff)
            {
                throw new ArgumentException("Argument_ArrayTooLarge", "values");
            }
            int valuesLength = values.Length;
            this.m_array = new int[valuesLength];
            this.m_length = values.Length * 0x20;
            Array.Copy(values, this.m_array, valuesLength);
            this._version = 0;
        }

        /// <summary>
        /// Initializes a new instance of the BitArray class that can hold the specified number of bit values, which are initially set to false.
        /// </summary>
        /// <param name="length"></param>
        public BitArray(int length)
            : this(length, false)
        {
        }

        /// <summary>
        /// Initializes a new instance of the BitArray class that can hold the specified number of bit values, which are initially set to the specified value.
        /// </summary>
        /// <param name="length"></param>
        /// <param name="defaultValue"></param>
        public BitArray(int length, bool defaultValue)
        {
            if (length < 0)
            {
                throw new ArgumentOutOfRangeException("length", "ArgumentOutOfRange_NeedNonNegNum");
            }
            int len = GetArrayLength(length, 0x20);
            this.m_array = new int[len];
            this.m_length = length;
            int num = defaultValue ? -1 : 0;
            for (int i = 0; i < len; ++i)
            {
                this.m_array[i] = num;
            }
            this._version = 0;
        }

        /// <summary>
        /// Creates a shallow copy of the BitArray.
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            return new BitArray(this.m_array) { _version = this._version, m_length = this.m_length };
        }

        /// <summary>
        /// Copies the entire BitArray to a compatible one-dimensional Array, starting at the specified index of the target array.
        /// </summary>
        /// <param name="array"></param>
        /// <param name="index"></param>
        public void CopyTo(Array array, int index)
        {
            if (array == null)
                throw new ArgumentNullException("array");
            else if (index < 0)
                throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_NeedNonNegNum");         
            if (array is int[]) Array.Copy(this.m_array, 0, array, index, GetArrayLength(this.m_length, 0x20));
            else if (array is byte[])
            {
                int arrayLength = GetArrayLength(this.m_length, 8);
                if ((array.Length - index) < arrayLength)
                    throw new ArgumentException("Argument_InvalidOffLen");
                byte[] buffer = (byte[])array;
                for (int i = 0; i < arrayLength; ++i)
                    buffer[index + i] = (byte)((this.m_array[i / 4] >> ((i % 4) * 8)) & 0xff);
            }
            //Must be Boolean
            else if (!(array is bool[]))
                throw new ArgumentException("Arg_BitArrayTypeUnsupported");
            else if ((array.Length - index) < this.m_length)
                throw new ArgumentException("Argument_InvalidOffLen");
            else
            {                
                bool[] flagArray = (bool[])array;
                for (int j = 0; j < this.m_length; ++j) flagArray[index + j] = ((this.m_array[j / 0x20] >> (j % 0x20)) & 1) != 0;
            }
        }

        /// <summary>
        /// Gets the value of the bit at a specific position in the BitArray.
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public bool Get(int index)
        {
            if ((index < 0) || (index >= this.Length))
                throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index");
            return ((this.m_array[index / 0x20] & (((int)1) << (index % 0x20))) != 0);
        }

        static int GetArrayLength(int n, int div)
        {
            return n <= 0 ? 0 : (((n - 1) / div) + 1);
        }

        /// <summary>
        /// Returns an enumerator that iterates through the BitArray.
        /// </summary>
        /// <returns></returns>
        public System.Collections.IEnumerator GetEnumerator()
        {
            return new BitArrayEnumeratorSimple(this);
        }

        /// <summary>
        /// Inverts all the bit values in the given BitArray, so that elements set to true are changed to false, and elements set to false are changed to true.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public BitArray Not(BitArray value)
        {
            if (value == null)
                throw new ArgumentNullException("value");
            if (this.Length != value.Length)
                throw new ArgumentException("Arg_ArrayLengthsDiffer");
            int arrayLength = GetArrayLength(this.m_length, 0x20);
            for (int i = 0; i < arrayLength; ++i) this.m_array[i] = ~value.m_array[i];
            ++this._version;
            return this;
        }

        /// <summary>
        /// Inverts all the bit values in the current BitArray, so that elements set to true are changed to false, and elements set to false are changed to true.
        /// </summary>
        /// <returns></returns>
        public BitArray Not() { return Not(this); }

        /// <summary>
        /// Performs the bitwise OR operation on the elements in the current BitArray against the corresponding elements in the specified BitArray.
        /// </summary>
        /// <returns></returns>
        public BitArray Or() { return Or(this); }

        /// <summary>
        /// Performs the bitwise OR operation on the elements in the given BitArray against the corresponding elements in the specified BitArray.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public BitArray Or(BitArray value)
        {
            if (value == null)
                throw new ArgumentNullException("value");
            if (this.Length != value.Length)
                throw new ArgumentException("Arg_ArrayLengthsDiffer");
            int arrayLength = GetArrayLength(this.m_length, 0x20);
            for (int i = 0; i < arrayLength; ++i) this.m_array[i] |= value.m_array[i];//~&=
            ++this._version;
            return this;
        }

        /// <summary>
        /// Performs the bitwise AND operation on the elements in the current BitArray against the corresponding elements in the specified BitArray.
        /// </summary>
        /// <returns></returns>
        public BitArray And() { return And(this); }

        /// <summary>
        /// Performs the bitwise AND operation on the elements in the given BitArray against the corresponding elements in the specified BitArray.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public BitArray And(BitArray value)
        {
            if (value == null)
                throw new ArgumentNullException("value");
            if (this.Length != value.Length)
                throw new ArgumentException("Arg_ArrayLengthsDiffer");
            int arrayLength = GetArrayLength(this.m_length, 0x20);
            for (int i = 0; i < arrayLength; ++i) this.m_array[i] &= value.m_array[i];
            ++this._version;
            return this;
        }

        /// <summary>
        /// Sets the bit at a specific position in the BitArray to the specified value.
        /// </summary>
        /// <param name="index"></param>
        /// <param name="value"></param>
        public void Set(int index, bool value)
        {
            if ((index < 0) || (index >= this.Length))
                throw new ArgumentOutOfRangeException("index", "ArgumentOutOfRange_Index");
            else if (value) this.m_array[index / 0x20] |= ((int)1) << (index % 0x20);
            else this.m_array[index / 0x20] &= ~(((int)1) << (index % 0x20));
            ++this._version;
        }

        /// <summary>
        /// Sets all bits in the BitArray to the specified value.
        /// </summary>
        /// <param name="value"></param>
        public void SetAll(bool value)
        {
            int num = value ? -1 : 0;
            int arrayLength = GetArrayLength(this.m_length, 0x20);
            for (int i = 0; i < arrayLength; ++i) this.m_array[i] = num;
            ++this._version;
        }

        public BitArray Xor(BitArray value)
        {
            if (value == null)
                throw new ArgumentNullException("value");
            else if (this.Length != value.Length)
                throw new ArgumentException("Arg_ArrayLengthsDiffer");
            int arrayLength = GetArrayLength(this.m_length, 0x20);
            for (int i = 0; i < arrayLength; ++i) this.m_array[i] ^= value.m_array[i];
            ++this._version;
            return this;
        }

        /// <summary>
        /// Performs the Not and And operations on the current BitArray
        /// </summary>
        /// <returns></returns>
        public BitArray Nand() { return Nand(this); }

        /// <summary>
        /// Performs the Not and And operations on the given BitArray
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public BitArray Nand(BitArray value) { return And(value.Not()); }

        /// <summary>
        /// Performs the Not and Or operations on the current BitArray
        /// </summary>
        /// <returns></returns>
        public BitArray Nor() { return Nor(this); }

        /// <summary>
        /// Performs the Not and Or operations on the given BitArray
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public BitArray Nor(BitArray value) { return Or(value.Not()); }

        /// <summary>
        /// Gets the number of elements contained in the BitArray.
        /// </summary>
        public int Count
        {
            get
            {
                return this.m_length;
            }
        }

        /// <summary>
        /// Gets a value indicating whether the BitArray is read-only.
        /// </summary>
        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets a value indicating whether access to the BitArray is synchronized (thread safe).
        /// </summary>
        public bool IsSynchronized
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Gets or sets the value of the bit at a specific position in the BitArray.
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public bool this[int index]
        {
            get
            {
                return this.Get(index);
            }
            set
            {
                this.Set(index, value);
            }
        }

        /// <summary>
        /// Gets or sets the number of elements in the BitArray.
        /// </summary>
        public int Length
        {
            get
            {
                return this.m_length;
            }
            set
            {
                if (value < 0)
                    throw new ArgumentOutOfRangeException("value", "ArgumentOutOfRange_NeedNonNegNum");
                int arrayLength = GetArrayLength(value, 0x20);
                if ((arrayLength > this.m_array.Length) || ((arrayLength + 0x100) < this.m_array.Length))
                {
                    int[] destinationArray = new int[arrayLength];
                    Array.Copy(this.m_array, destinationArray, (arrayLength > this.m_array.Length) ? this.m_array.Length : arrayLength);
                    this.m_array = destinationArray;
                }
                if (value > this.m_length)
                {
                    int index = GetArrayLength(this.m_length, 0x20) - 1;
                    int num3 = this.m_length % 0x20;
                    if (num3 > 0) this.m_array[index] &= (((int)1) << num3) - 1;
                    Array.Clear(this.m_array, index + 1, (arrayLength - index) - 1);
                }
                this.m_length = value;
                ++this._version;
            }
        }

        /// <summary>
        /// Gets an object that can be used to synchronize access to the BitArray.
        /// </summary>
        public object SyncRoot
        {
            get
            {                
                return this._syncRoot;
            }
        }

        [Serializable]
        class BitArrayEnumeratorSimple : System.Collections.IEnumerator, ICloneable
        {
             BitArray bitarray;
             bool currentElement;
             int index;
             int version;

            internal BitArrayEnumeratorSimple(BitArray bitarray)
            {
                this.bitarray = bitarray;
                this.index = -1;
                this.version = bitarray._version;
            }

            //[SecuritySafeCritical]
            public object Clone()
            {
                return base.MemberwiseClone();
            }

            public virtual bool MoveNext()
            {
                if (this.version != this.bitarray._version)
                {
                    throw new InvalidOperationException("InvalidOperation_EnumFailedVersion");
                }
                if (this.index < (this.bitarray.Count - 1))
                {
                    this.currentElement = this.bitarray.Get(++this.index);
                    return true;
                }
                this.index = this.bitarray.Count;
                return false;
            }

            public void Reset()
            {
                if (this.version != this.bitarray._version)
                {
                    throw new InvalidOperationException("InvalidOperation_EnumFailedVersion");
                }
                this.index = -1;
            }

            public virtual object Current
            {
                get
                {
                    if (this.index == -1)
                    {
                        throw new InvalidOperationException("InvalidOperation_EnumNotStarted");
                    }
                    if (this.index >= this.bitarray.Count)
                    {
                        throw new InvalidOperationException("InvalidOperation_EnumEnded");
                    }
                    return this.currentElement;
                }
            }
        }
    }
}
